#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>


#include "drm_plane_drv.h"

int fd_all=0;
uint32_t crtc_id_all=40;

int drm_set_plane_prop(unsigned int plane_id, const char *prop_name, int prop_val)
{
    int fd;
    if(fd_all==0){
        fd_all = open(DRM_CARD, O_RDWR | O_CLOEXEC);
        if(fd < 0)
        {
            fprintf(stderr,"open /dev/dri/card0 fail");
            return -1;
        }
    }
    fd=fd_all;
    drmModeObjectPropertiesPtr props;
    int ret = -1;

    props = drmModeObjectGetProperties(fd, plane_id, DRM_MODE_OBJECT_PLANE);
    if (!props) {
        return ret;
    }

    for (size_t i = 0; i < props->count_props; i++) {
        drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[i]);

        if (!strcmp(prop->name, prop_name)) {
            ret = drmModeObjectSetProperty(fd, plane_id,
                                           DRM_MODE_OBJECT_PLANE,
                                           prop->prop_id,
                                           prop_val);
            drmModeFreeProperty(prop);
            break;
        }
        drmModeFreeProperty(prop);
    }
    drmModeFreeObjectProperties(props);

    return ret;
}

int bo_create_dumb(int fd, struct buffer_object *bo,unsigned int width, unsigned int height, unsigned int bpp)
{
    struct drm_mode_create_dumb arg;
//    struct buffer_object *bo;
    int ret;

//    bo = calloc(1, sizeof(*bo));
//    if (bo == NULL) {
//        fprintf(stderr, "failed to allocate buffer object\n");
//        return NULL;
//    }

    memset(&arg, 0, sizeof(arg));
    arg.bpp = bpp;
    arg.width = width;
    arg.height = height;

    ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg);
    if (ret) {
        fprintf(stderr, "failed to create dumb buffer: %s\n",
            strerror(errno));
        free(bo);
        return 0;
    }

    bo->fd = fd;
    bo->handle = arg.handle;
    bo->size = arg.size;
    bo->pitch = arg.pitch;

    return 0;
}


void bo_create(int fd,struct buffer_object *bo, unsigned int format,
      unsigned int width, unsigned int height,
      unsigned int handles[4], unsigned int pitches[4],
      unsigned int offsets[4])
{
    unsigned int virtual_height;
//    struct buffer_object *bo;
    unsigned int bpp;
    void *virtual;
    int ret;

    switch (format) {
    case DRM_FORMAT_C8:
    case DRM_FORMAT_NV12:
    case DRM_FORMAT_NV21:
    case DRM_FORMAT_NV16:
    case DRM_FORMAT_NV61:
    case DRM_FORMAT_YUV420:
    case DRM_FORMAT_YVU420:
        bpp = 8;
        break;

    case DRM_FORMAT_ARGB4444:
    case DRM_FORMAT_XRGB4444:
    case DRM_FORMAT_ABGR4444:
    case DRM_FORMAT_XBGR4444:
    case DRM_FORMAT_RGBA4444:
    case DRM_FORMAT_RGBX4444:
    case DRM_FORMAT_BGRA4444:
    case DRM_FORMAT_BGRX4444:
    case DRM_FORMAT_ARGB1555:
    case DRM_FORMAT_XRGB1555:
    case DRM_FORMAT_ABGR1555:
    case DRM_FORMAT_XBGR1555:
    case DRM_FORMAT_RGBA5551:
    case DRM_FORMAT_RGBX5551:
    case DRM_FORMAT_BGRA5551:
    case DRM_FORMAT_BGRX5551:
    case DRM_FORMAT_RGB565:
    case DRM_FORMAT_BGR565:
    case DRM_FORMAT_UYVY:
    case DRM_FORMAT_VYUY:
    case DRM_FORMAT_YUYV:
    case DRM_FORMAT_YVYU:
        bpp = 16;
        break;

    case DRM_FORMAT_BGR888:
    case DRM_FORMAT_RGB888:
        bpp = 24;
        break;

    case DRM_FORMAT_ARGB8888:
    case DRM_FORMAT_XRGB8888:
    case DRM_FORMAT_ABGR8888:
    case DRM_FORMAT_XBGR8888:
    case DRM_FORMAT_RGBA8888:
    case DRM_FORMAT_RGBX8888:
    case DRM_FORMAT_BGRA8888:
    case DRM_FORMAT_BGRX8888:
    case DRM_FORMAT_ARGB2101010:
    case DRM_FORMAT_XRGB2101010:
    case DRM_FORMAT_ABGR2101010:
    case DRM_FORMAT_XBGR2101010:
    case DRM_FORMAT_RGBA1010102:
    case DRM_FORMAT_RGBX1010102:
    case DRM_FORMAT_BGRA1010102:
    case DRM_FORMAT_BGRX1010102:
        bpp = 32;
        break;

    case DRM_FORMAT_XRGB16161616F:
    case DRM_FORMAT_XBGR16161616F:
    case DRM_FORMAT_ARGB16161616F:
    case DRM_FORMAT_ABGR16161616F:
        bpp = 64;
        break;

    default:
        fprintf(stderr, "unsupported format 0x%08x\n",  format);
//        return NULL;
    }

    switch (format) {
    case DRM_FORMAT_NV12:
    case DRM_FORMAT_NV21:
    case DRM_FORMAT_YUV420:
    case DRM_FORMAT_YVU420:
        virtual_height = height * 3 / 2;
        break;

    case DRM_FORMAT_NV16:
    case DRM_FORMAT_NV61:
        virtual_height = height * 2;
        break;

    default:
        virtual_height = height;
        break;
    }

    bo_create_dumb(fd,bo, width, virtual_height, bpp);

    /* just testing a limited # of formats to test single
     * and multi-planar path.. would be nice to add more..
     */
    switch (format) {
    case DRM_FORMAT_UYVY:
    case DRM_FORMAT_VYUY:
    case DRM_FORMAT_YUYV:
    case DRM_FORMAT_YVYU:
        offsets[0] = 0;
        handles[0] = bo->handle;
        pitches[0] = bo->pitch;

        break;

    case DRM_FORMAT_NV12:
    case DRM_FORMAT_NV21:
    case DRM_FORMAT_NV16:
    case DRM_FORMAT_NV61:
        offsets[0] = 0;
        handles[0] = bo->handle;
        pitches[0] = bo->pitch;
        pitches[1] = pitches[0];
        offsets[1] = pitches[0] * height;
        handles[1] = bo->handle;

        break;

    case DRM_FORMAT_YUV420:
    case DRM_FORMAT_YVU420:
        offsets[0] = 0;
        handles[0] = bo->handle;
        pitches[0] = bo->pitch;
        pitches[1] = pitches[0] / 2;
        offsets[1] = pitches[0] * height;
        handles[1] = bo->handle;
        pitches[2] = pitches[1];
        offsets[2] = offsets[1] + pitches[1] * height / 2;
        handles[2] = bo->handle;

        break;

    case DRM_FORMAT_C8:
    case DRM_FORMAT_ARGB4444:
    case DRM_FORMAT_XRGB4444:
    case DRM_FORMAT_ABGR4444:
    case DRM_FORMAT_XBGR4444:
    case DRM_FORMAT_RGBA4444:
    case DRM_FORMAT_RGBX4444:
    case DRM_FORMAT_BGRA4444:
    case DRM_FORMAT_BGRX4444:
    case DRM_FORMAT_ARGB1555:
    case DRM_FORMAT_XRGB1555:
    case DRM_FORMAT_ABGR1555:
    case DRM_FORMAT_XBGR1555:
    case DRM_FORMAT_RGBA5551:
    case DRM_FORMAT_RGBX5551:
    case DRM_FORMAT_BGRA5551:
    case DRM_FORMAT_BGRX5551:
    case DRM_FORMAT_RGB565:
    case DRM_FORMAT_BGR565:
    case DRM_FORMAT_BGR888:
    case DRM_FORMAT_RGB888:
    case DRM_FORMAT_ARGB8888:
    case DRM_FORMAT_XRGB8888:
    case DRM_FORMAT_ABGR8888:
    case DRM_FORMAT_XBGR8888:
    case DRM_FORMAT_RGBA8888:
    case DRM_FORMAT_RGBX8888:
    case DRM_FORMAT_BGRA8888:
    case DRM_FORMAT_BGRX8888:
    case DRM_FORMAT_ARGB2101010:
    case DRM_FORMAT_XRGB2101010:
    case DRM_FORMAT_ABGR2101010:
    case DRM_FORMAT_XBGR2101010:
    case DRM_FORMAT_RGBA1010102:
    case DRM_FORMAT_RGBX1010102:
    case DRM_FORMAT_BGRA1010102:
    case DRM_FORMAT_BGRX1010102:
    case DRM_FORMAT_XRGB16161616F:
    case DRM_FORMAT_XBGR16161616F:
    case DRM_FORMAT_ARGB16161616F:
    case DRM_FORMAT_ABGR16161616F:
        offsets[0] = 0;
        handles[0] = bo->handle;
        pitches[0] = bo->pitch;

        break;
    }

//    return bo;
}

struct buffer_object *
modeset_create_fb(int fd,struct plane_arg *p)
{
    struct buffer_object *bo;
    uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
    struct drm_mode_map_dumb map = {};

    bo=calloc(1,sizeof(*p->bo));
        if (bo == NULL) {
            fprintf(stderr, "failed to allocate buffer object\n");
            return NULL;
        }
    bo_create(fd,bo,p->fourcc,p->w,p->h,handles,pitches,offsets);

    /* just use single plane format for now.. */
    if (drmModeAddFB2(fd, p->w, p->h, p->fourcc,
            handles, pitches, offsets, &p->fb_id, 0)) {
        fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
        return -1;
    }

    map.handle = bo->handle;
    drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);

    bo->vaddr = mmap(0, bo->size, PROT_READ | PROT_WRITE,
            MAP_SHARED, fd, map.offset);

    memset(bo->vaddr, 0xff, bo->size);

//    munmap(bo->vaddr, bo->size);

    return 0;
}

//gwj:destroy one buf;
void bo_destroy(struct buffer_object *bo)
{
    struct drm_mode_destroy_dumb arg;
    int ret;

    memset(&arg, 0, sizeof(arg));
    arg.handle = bo->handle;

    ret = drmIoctl(bo->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg);
    if (ret)
        fprintf(stderr, "failed to destroy dumb buffer: %s\n",
            strerror(errno));
    munmap(bo->vaddr, bo->size);

    free(bo);
}
//gwj:destroy more buf;
void drm_bufs_destroy(struct buffer_object *bufs,int buf_num)
{
    struct buffer_object *bo;
    struct drm_mode_destroy_dumb arg;
    int ret;

    for(int i=0;i<buf_num;i++){
        bo=&bufs[i];
        memset(&arg, 0, sizeof(arg));
        arg.handle = bo->handle;
        ret = drmIoctl(bo->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg);
        if (ret)
            fprintf(stderr, "failed to destroy dumb buffer: %s\n",
                strerror(errno));
        munmap(bo->vaddr, bo->size);
    }
    free(bufs);
}


int init_drm_bufs(int fd,struct drm_buffers *drm_bufs,int32_t width,int32_t height,int buf_num,unsigned int fourcc){
    drm_bufs->buf_num=buf_num;
    drm_bufs->height=height;
    drm_bufs->width=width;
    drm_bufs->fourcc=fourcc;
    drm_bufs->bo=calloc(buf_num,sizeof(*drm_bufs->bo));
    if (drm_bufs->bo == NULL) {
        fprintf(stderr, "failed to allocate buffer object\n");
        return NULL;
    }
    printf("sieof :%d  %d\r\n",sizeof(*drm_bufs->bo),sizeof(struct drm_buffers));

    uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
    struct drm_mode_map_dumb map = {};

    for(int i=0;i<buf_num;i++){
        struct buffer_object *bo=&drm_bufs->bo[i];
        bo_create(fd,bo,fourcc,width,height,handles,pitches,offsets);

        /* just use single plane format for now.. */
        if (drmModeAddFB2(fd, width, height, fourcc,
                handles, pitches, offsets, &(bo->fb_id), 0)) {
            fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
            return -1;
        }

        map.handle = bo->handle;
        drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);

        bo->vaddr = mmap(0, bo->size, PROT_READ | PROT_WRITE,
                MAP_SHARED, fd, map.offset);

        memset(bo->vaddr, 0xff, bo->size);
    }

    return 0;
}

int plane_bind_buf(int fd, struct plane_arg *p,struct buffer_object plane_bo)
{
    uint32_t plane_id;
    uint32_t crtc_id;
    int crtc_x, crtc_y, crtc_w, crtc_h;

    plane_id = p->plane_id;
    crtc_id=p->crtc_id;
    crtc_w = p->w * p->scale;
    crtc_h = p->h * p->scale;
    crtc_x = p->x;
    crtc_y = p->y;
    /* note src coords (last 4 args) are in Q16 format */
    if (drmModeSetPlane(fd, plane_id, crtc_id, plane_bo.fb_id,
                0, crtc_x, crtc_y, crtc_w, crtc_h,
                0, 0, p->w << 16, p->h << 16)) {
        fprintf(stderr, "failed to enable plane,maby color buffer format not correct:%s\n",
            strerror(errno));
        return -1;
    }
    return 0;
}
static void dump_fourcc(uint32_t fourcc)
{
    printf(" %c%c%c%c",
        fourcc,
        fourcc >> 8,
        fourcc >> 16,
        fourcc >> 24);
};
//打印drm分层信息。包括层id以及对应层支持的显示格式。
// crtc id : 38 
// Planes:
// id	crtc	fb	CRTC x,y	x,y	gamma size	possible crtcs
// 33	0	0	0,0		0,0	0       	0x00000001
//   formats: NV12
// 34	0	0	0,0		0,0	0       	0x00000001
//   formats: NV12
// 35	0	0	0,0		0,0	0       	0x00000001
//   formats: NV12
// 36	0	0	0,0		0,0	0       	0x00000001
//   formats: BG24
// 37	38	48	0,0		0,0	0       	0x00000001
//   formats: RG24
int drm_print(){
    drmModeConnector *conn;
    drmModeRes *res;
    drmModePlaneRes *plane_res;
    int crtc_id,conn_id,fd;

    fd = open(DRM_CARD, O_RDWR | O_CLOEXEC);
    if(fd < 0)
    {
        printf("open /dev/dri/card fail");
        return -1;
    }
    res = drmModeGetResources(fd);
    if(!res)
    {
        printf("drmModeGetResources fail");
        return -1;
    }
    crtc_id = res->crtcs[0];
    conn_id = res->connectors[0];
    printf("crtc id : %d \r\n",crtc_id);
    drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
    plane_res = drmModeGetPlaneResources(fd);
    if(!plane_res)
    {
        printf("drmModeGetPlaneResources fail");
        return -2;
    }
    printf("Planes:\n");
        printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
    for(int i=0;i<plane_res->count_planes;i++){
//        struct plane *plane = plane_res->planes[i];
        drmModePlane *ovr = drmModeGetPlane(fd,plane_res->planes[i]);
        if (!ovr)
            continue;
        printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
               ovr->plane_id, ovr->crtc_id, ovr->fb_id,
               ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
               ovr->gamma_size, ovr->possible_crtcs);

        if (!ovr->count_formats)
            continue;

        printf("  formats:");
        for (int j = 0; j < ovr->count_formats; j++)
            dump_fourcc(ovr->formats[j]);
        printf("\n");
    }
    close(fd);
}

int drm_crtc_id_init(){
    drmModeConnector *conn;
    drmModeRes *res;
    drmModePlaneRes *plane_res;
    int crtc_id,conn_id,fd;

    fd = open(DRM_CARD, O_RDWR | O_CLOEXEC);
    if(fd < 0)
    {
        printf("open /dev/dri/card0 fail");
        return -1;
    }
    res = drmModeGetResources(fd);
    if(!res)
    {
        printf("drmModeGetResources fail");
        return -1;
    }
    crtc_id_all = res->crtcs[0];
    printf("crtc id is :%d",crtc_id_all);
    fflush(stdout);
    close(fd);
    return 0;
};
//设置drm分层的透明度属性。范围0-255.
int drm_alpha_init(){
    drm_set_plane_prop( 33, "alpha", 180);//yuv
    drm_set_plane_prop( 34, "alpha", 180);//yuv
    drm_set_plane_prop( 35, "alpha", 160);//yuv
    drm_set_plane_prop( 36, "alpha", 160);//yuv
//    drm_set_plane_prop( 37, "alpha", 160);//yuv
//    drm_set_plane_prop( 38, "alpha", 180);//RGB
    return 0;
};
//初始化plane[0]，绑定drm显示plane id：33,crtc id：38，plane层大小为1280x720，plane层位置为0，0.plane层格式为DRM_FORMAT_NV12
//调用示例 init_plane(&plane[0],33,38,1280,720,0,0,DRM_FORMAT_NV12);
int init_plane(struct plane_arg *plane,uint32_t plane_id, uint32_t width, uint32_t height,int32_t x,int32_t y,unsigned int fourcc)
{
//    plane->fd=fd;
    if(plane->bo!=NULL){
        fprintf(stderr,"plane already init\r\n");
        return -1;
    }

    plane->plane_id = plane_id;

    plane->crtc_id = crtc_id_all;

    plane->w = width;
    plane->h = height;
    plane->x=x;
    plane->y=y;
    plane->scale = 1.0;
    plane->fourcc = fourcc;
    plane->buf_num=2;

    if(fd_all==0){
        fd_all = open(DRM_CARD, O_RDWR | O_CLOEXEC);
        if(fd_all < 0)
        {
            fprintf(stderr,"open /dev/dri/card0 fail");
            return -1;
        }
    }
    plane->fd=fd_all;
    set_plane(plane);
    return 0;
}
int set_plane( struct plane_arg *p)
{
    uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
    uint32_t plane_id;
    uint32_t crtc_id;
    struct buffer_object *plane_bo;
    uint32_t plane_flags = 0;
    int crtc_x, crtc_y, crtc_w, crtc_h;
    struct drm_mode_create_dumb create = {};
    struct drm_mode_map_dumb map = {};
    int fd=p->fd;


    plane_id = p->plane_id;
    crtc_id=p->crtc_id;
    p->bo=NULL;
    p->bo=calloc(p->buf_num,sizeof(*p->bo));
//    printf("gjw-size::%d \r\n",sizeof(*p->bo));
    if (p->bo == NULL) {
        fprintf(stderr, "failed to allocate buffer object\n");
        return NULL;
    }

    for(int i=0;i<p->buf_num;i++){
        plane_bo=&(p->bo[i]);
        plane_bo->fb_id=0;
        plane_bo->vaddr=NULL;
        plane_bo->size=0;
        bo_create(fd,plane_bo, p->fourcc, p->w, p->h, handles,
                     pitches, offsets);
        plane_bo->fd=fd;
        map.handle = plane_bo->handle;
        if (drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map)){
            sprintf(stderr,"DRM_IOCTL_MODE_MAP_DUMB erro");
            free(p->bo);
            p->bo=NULL;
            return -1;
        }
        plane_bo->vaddr = mmap(0, plane_bo->size, PROT_READ | PROT_WRITE,
                MAP_SHARED, fd, map.offset);

        memset(plane_bo->vaddr, 0x55, plane_bo->size);
        /* just use single plane format for now.. */
        if (drmModeAddFB2(fd, p->w, p->h, p->fourcc,
                handles, pitches, offsets, &plane_bo->fb_id, plane_flags)) {
            fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
            free(p->bo);
            p->bo=NULL;
            return -1;
        }
//        printf("gwj---fb_id:%d addr:%ld size :%d\r\n",plane_bo->fb_id,plane_bo->vaddr,plane_bo->size);
    }

    switch_plane_buffer(p,0);
    return 0;
}
//更新plane[0]的显存空间。
int plane_cpy(struct plane_arg *p,void * addr){
    if(p->bo!=NULL){
        memcpy(p->bo[0].vaddr,addr,p->bo[0].size);
    }
    else{
        fprintf(stderr,"plane not init\r\n");
    }
}
int switch_plane_buffer(struct plane_arg *p,unsigned char buf_count){
    int crtc_x, crtc_y, crtc_w, crtc_h;
    uint32_t plane_id;
    uint32_t crtc_id;
    uint32_t plane_flags = 0;
    int fd=p->fd;
    plane_id = p->plane_id;
    crtc_id=p->crtc_id;
    crtc_w = p->w * p->scale;
    crtc_h = p->h * p->scale;
    crtc_x = p->x;
    crtc_y = p->y;
    if((p->buf_num>buf_count)&&(buf_count>=0)){
        p->fb_id=p->bo[buf_count].fb_id;
    }
    else{
        return -1;
    }

    /* note src coords (last 4 args) are in Q16 format */
    if (drmModeSetPlane(fd, plane_id, crtc_id, p->fb_id,
                plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
                0, 0, p->w << 16, p->h << 16)) {
        if(errno==2){
            fprintf(stderr, "failed to enable plane,maby crtc id not correct:%d,%s\n",
                errno,strerror(errno));
        }
        else if (errno==22) {
            fprintf(stderr, "failed to enable plane,maby plane color buffer format not correct:%d,%s\n",
                errno,strerror(errno));
        }
        else{
            fprintf(stderr, "failed to enable plane:%5d,%s\n",
                errno,strerror(errno));
        }
        return -1;
    }
    return 0;
}

int plane_destroy(struct plane_arg *plane){
    struct buffer_object *bo;
    struct drm_mode_destroy_dumb arg;
    int ret;
    int buf_num;
    buf_num=plane->buf_num;

    if(plane->bo==NULL){
        fprintf(stderr, "failed to destroy plane,plane not init\n");
        return -1;
    }

    for(int i=0;i<buf_num;i++){
        bo=&(plane->bo[i]);
//        p->fb_id=p->bo[buf_count].fb_id;
        if(bo->fb_id==0){
            break;
        }
        drmModeRmFB(plane->fd,bo->fb_id);
        memset(&arg, 0, sizeof(arg));
        arg.handle = bo->handle;
        ret = drmIoctl(bo->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg);
        if (ret)
            fprintf(stderr, "failed to destroy dumb buffer: %s\n",
                strerror(errno));
        munmap(bo->vaddr, bo->size);
        bo->vaddr=NULL;
        bo->size=NULL;
    }

    free(plane->bo);
    plane->bo=NULL;
    return 0;
}


int maindgdfgf(int argc, char **argv)
{
    struct plane_arg plane;
    int fd;
//    im

    init_plane(&plane,33,720,720,0,0,DRM_FORMAT_NV12);
    memset(plane.bo->vaddr,0x55,plane.bo->size);

    set_plane(&plane);
    sleep(5);
    memset(plane.bo->vaddr,0xff,plane.bo->size);
    sleep(5);
    bo_destroy(plane.bo);
    close(fd);

    return 0;
}
